"""
订单相关验证器
提供订单创建、更新时的数据验证功能
"""
import re
from datetime import datetime, timedelta, date, time
from flask import current_app
from app import db

class OrderValidator:
    """订单数据验证器"""
    
    # 订单状态枚举
    VALID_ORDER_STATUSES = [0, 1, 2, 3, 4]  # 0=未支付, 1=已支付, 2=已取消, 3=已完成, 4=已退款
    
    # 预约时间限制
    MAX_ADVANCE_DAYS = 30  # 最多提前30天预约
    MIN_ADVANCE_HOURS = 1  # 最少提前1小时预约
    
    # 订单创建频率限制
    ORDER_LIMIT_PER_USER_DAY = 20  # 每用户每天最多创建20个订单
    
    @classmethod
    def validate_order_creation_data(cls, data):
        """验证订单创建数据"""
        if not data or not isinstance(data, dict):
            raise ValueError("订单数据不能为空")
        
        # 清理输入数据
        data = cls._clean_order_data(data)
        
        # 验证必要字段
        required_fields = ['venue_id', 'booking_date', 'start_time', 'end_time']
        for field in required_fields:
            if field not in data:
                raise ValueError(f"缺少必要参数: {field}")
        
        venue_id = data['venue_id']
        booking_date_str = data['booking_date']
        start_time_str = data['start_time']
        end_time_str = data['end_time']
        
        # 验证场馆ID
        cls._validate_venue_id(venue_id)
        
        # 验证并解析日期时间
        booking_date = cls._validate_and_parse_date(booking_date_str)
        start_time = cls._validate_and_parse_time(start_time_str)
        end_time = cls._validate_and_parse_time(end_time_str)
        
        # 验证时间逻辑
        cls._validate_time_logic(booking_date, start_time, end_time)
        
        # 验证预约时间的合理性
        cls._validate_booking_time_constraints(booking_date, start_time)
        
        # 验证其他可选参数
        if 'participant_count' in data:
            cls._validate_participant_count(data['participant_count'])
        
        if 'remark' in data:
            cls._validate_remark(data['remark'])
        
        return {
            'venue_id': venue_id,
            'booking_date': booking_date,
            'start_time': start_time,
            'end_time': end_time,
            'participant_count': data.get('participant_count', 1),
            'remark': data.get('remark', '').strip()
        }
    
    @classmethod
    def _clean_order_data(cls, data):
        """清理订单数据"""
        cleaned_data = {}
        for key, value in data.items():
            if isinstance(value, str):
                cleaned_data[key] = value.strip()
            else:
                cleaned_data[key] = value
        return cleaned_data
    
    @classmethod
    def _validate_venue_id(cls, venue_id):
        """验证场馆ID"""
        if not venue_id:
            raise ValueError("场馆ID不能为空")
        
        try:
            venue_id = int(venue_id)
        except (ValueError, TypeError):
            raise ValueError("场馆ID格式错误")
        
        if venue_id <= 0:
            raise ValueError("场馆ID必须大于0")
        
        return venue_id
    
    @classmethod
    def _validate_and_parse_date(cls, date_str):
        """验证并解析日期"""
        if not date_str:
            raise ValueError("预约日期不能为空")
        
        try:
            booking_date = datetime.strptime(date_str, '%Y-%m-%d').date()
        except ValueError:
            raise ValueError("日期格式错误，请使用YYYY-MM-DD格式")
        
        return booking_date
    
    @classmethod
    def _validate_and_parse_time(cls, time_str):
        """验证并解析时间"""
        if not time_str:
            raise ValueError("时间不能为空")
        
        # 支持 HH:MM 和 HH:MM:SS 格式
        time_formats = ['%H:%M', '%H:%M:%S']
        
        for time_format in time_formats:
            try:
                return datetime.strptime(time_str, time_format).time()
            except ValueError:
                continue
        
        raise ValueError("时间格式错误，请使用HH:MM或HH:MM:SS格式")
    
    @classmethod
    def _validate_time_logic(cls, booking_date, start_time, end_time):
        """验证时间逻辑"""
        if start_time >= end_time:
            raise ValueError("结束时间必须晚于开始时间")
        
        # 计算时长，不能超过12小时
        start_datetime = datetime.combine(booking_date, start_time)
        end_datetime = datetime.combine(booking_date, end_time)
        duration = end_datetime - start_datetime
        
        if duration > timedelta(hours=12):
            raise ValueError("单次预约时长不能超过12小时")
        
        if duration < timedelta(minutes=30):
            raise ValueError("单次预约时长不能少于30分钟")
    
    @classmethod
    def _validate_booking_time_constraints(cls, booking_date, start_time):
        """验证预约时间约束"""
        now = datetime.now()
        booking_datetime = datetime.combine(booking_date, start_time)
        
        # 不能预约过去的时间
        if booking_datetime <= now:
            raise ValueError("不能预约过去的时间")
        
        # 不能预约太远的未来时间
        max_advance_date = now + timedelta(days=cls.MAX_ADVANCE_DAYS)
        if booking_datetime > max_advance_date:
            raise ValueError(f"不能预约超过{cls.MAX_ADVANCE_DAYS}天的时间")
        
        # 必须提前一定时间预约
        min_advance_time = now + timedelta(hours=cls.MIN_ADVANCE_HOURS)
        if booking_datetime < min_advance_time:
            raise ValueError(f"请至少提前{cls.MIN_ADVANCE_HOURS}小时预约")
    
    @classmethod
    def _validate_participant_count(cls, count):
        """验证参与人数"""
        if count is None:
            return 1
        
        try:
            count = int(count)
        except (ValueError, TypeError):
            raise ValueError("参与人数必须是数字")
        
        if count <= 0:
            raise ValueError("参与人数必须大于0")
        
        if count > 50:
            raise ValueError("参与人数不能超过50人")
        
        return count
    
    @classmethod
    def _validate_remark(cls, remark):
        """验证备注信息"""
        if not remark:
            return ""
        
        if not isinstance(remark, str):
            raise ValueError("备注必须是字符串")
        
        remark = remark.strip()
        
        if len(remark) > 200:
            raise ValueError("备注长度不能超过200个字符")
        
        # 检查敏感词（简化实现）
        sensitive_words = ['政治', '黄赌毒', '违法', '犯罪']
        remark_lower = remark.lower()
        for word in sensitive_words:
            if word in remark_lower:
                raise ValueError(f"备注包含敏感词：{word}")
        
        return remark
    
    @classmethod
    def validate_venue_availability(cls, venue_id, booking_date, start_time, end_time, exclude_order_id=None):
        """验证场馆可用性"""
        from app.models.venue import Venue, VenueTimeSlot
        from app.models.order import Order
        
        # 验证场馆存在且可用
        venue = Venue.query.get(venue_id)
        if not venue:
            raise ValueError("场馆不存在")
        
        if venue.status != 1 or venue.is_deleted:
            raise ValueError("场馆已关闭或不可用")
        
        # 检查场馆是否在指定时间开放
        day_of_week = booking_date.isoweekday()  # 1-7 代表周一到周日
        
        # 将时间对象转换为字符串格式进行比较
        start_time_str = start_time.strftime('%H:%M')
        end_time_str = end_time.strftime('%H:%M')
        
        # 新的验证逻辑：检查预约时间段是否完全覆盖在场馆开放时间内
        # 查找该天所有开放的时间段
        day_time_slots = VenueTimeSlot.query.filter(
            VenueTimeSlot.venue_id == venue_id,
            VenueTimeSlot.day_of_week == day_of_week,
            VenueTimeSlot.status == 1
        ).order_by(VenueTimeSlot.start_time).all()
        
        if not day_time_slots:
            raise ValueError("场馆在该日期不开放")
        
        # 检查预约时间是否在场馆的任何开放时间内
        is_covered = False
        covering_slots = []
        
        # 找到所有覆盖预约时间段的开放时间段
        for slot in day_time_slots:
            slot_start = slot.start_time
            slot_end = slot.end_time
            
            # 检查时间段是否有交集
            if slot_start < end_time_str and slot_end > start_time_str:
                covering_slots.append(slot)
        
        if not covering_slots:
            raise ValueError("所选时间段场馆不开放")
        
        # 检查覆盖的时间段是否能完全包含预约时间
        # 对覆盖的时间段进行排序并检查连续性
        covering_slots.sort(key=lambda x: x.start_time)
        
        # 合并相邻或重叠的时间段
        merged_slots = []
        for slot in covering_slots:
            if not merged_slots:
                merged_slots.append((slot.start_time, slot.end_time))
            else:
                last_start, last_end = merged_slots[-1]
                if slot.start_time <= last_end:  # 相邻或重叠
                    # 合并时间段
                    merged_slots[-1] = (last_start, max(last_end, slot.end_time))
                else:
                    merged_slots.append((slot.start_time, slot.end_time))
        
        # 检查合并后的时间段是否能完全覆盖预约时间
        valid_slot = None
        for merged_start, merged_end in merged_slots:
            if merged_start <= start_time_str and merged_end >= end_time_str:
                is_covered = True
                # 找到第一个覆盖预约开始时间的时间段作为返回值
                for slot in covering_slots:
                    if slot.start_time <= start_time_str and slot.end_time > start_time_str:
                        valid_slot = slot
                        break
                break
        
        if not is_covered or not valid_slot:
            raise ValueError("所选时间段场馆不开放")
        
        # 检查时间段是否已被预约
        # 为了避免时间类型问题，我们使用原生SQL查询
        from sqlalchemy import text
        
        sql = text("""
            SELECT COUNT(*) as conflict_count
            FROM orders 
            WHERE venue_id = :venue_id 
            AND reservation_date = :reservation_date 
            AND status IN (0, 1, 3)
            AND (
                (TIME(start_time) < :end_time AND TIME(end_time) > :start_time)
            )
            AND (:exclude_order_id IS NULL OR id != :exclude_order_id)
        """)
        
        result = db.session.execute(sql, {
            'venue_id': venue_id,
            'reservation_date': booking_date,
            'start_time': start_time_str,
            'end_time': end_time_str,
            'exclude_order_id': exclude_order_id
        }).fetchone()
        
        conflict_count = result[0] if result else 0
        if conflict_count > 0:
            raise ValueError("该时间段已被预约")

        
        # 返回有效的时间段对象
        return valid_slot
    
    @classmethod
    def check_user_order_frequency(cls, user_id):
        """检查用户订单创建频率"""
        from app.models.order import Order
        
        today = date.today()
        today_orders = Order.query.filter(
            Order.user_id == user_id,
            db.func.date(Order.created_at) == today
        ).count()
        
        if today_orders >= cls.ORDER_LIMIT_PER_USER_DAY:
            raise ValueError(f"今日订单数量已达上限（{cls.ORDER_LIMIT_PER_USER_DAY}个）")
    
    @classmethod
    def check_user_unpaid_orders(cls, user_id, limit=100):
        """检查用户未支付订单数量"""
        from app.models.order import Order
        
        unpaid_orders = Order.query.filter(
            Order.user_id == user_id,
            Order.status == 0  # 未支付
        ).count()
        
        if unpaid_orders >= limit:
            raise ValueError(f"您有{unpaid_orders}个未支付订单，请先支付后再创建新订单")
    
    @classmethod
    def validate_order_cancellation(cls, order):
        """验证订单是否可以取消"""
        if not order:
            raise ValueError("订单不存在")
        
        if order.status != 0:  # 只有未支付订单可以取消
            raise ValueError("只有未支付订单可以取消")
        
        # 检查是否在取消时限内（预约时间前2小时）
        if order.reservation_date and order.start_time:
            reservation_datetime = datetime.combine(order.reservation_date, order.start_time)
            cancel_deadline = reservation_datetime - timedelta(hours=2)
            
            if datetime.now() > cancel_deadline:
                raise ValueError("预约时间前2小时内不能取消订单")
    
    @classmethod
    def validate_order_checkin(cls, order):
        """验证订单是否可以签到"""
        if not order:
            raise ValueError("订单不存在")
        
        if order.status != 1:  # 只有已支付订单可以签到
            raise ValueError("只有已支付订单可以签到")
        
        # 检查签到时间（预约时间前15分钟到结束时间）
        if order.reservation_date and order.start_time and order.end_time:
            now = datetime.now()
            reservation_date = order.reservation_date
            start_datetime = datetime.combine(reservation_date, order.start_time)
            end_datetime = datetime.combine(reservation_date, order.end_time)
            
            # 允许提前15分钟签到
            checkin_start = start_datetime - timedelta(minutes=15)
            
            if now < checkin_start:
                raise ValueError("签到时间未到，请在预约时间前15分钟内签到")
            
            if now > end_datetime:
                raise ValueError("预约时间已过，无法签到")
    
    @classmethod
    def generate_order_no(cls, prefix="VB"):
        """生成订单号"""
        import time
        timestamp = int(time.time() * 1000)  # 毫秒时间戳
        return f"{prefix}{timestamp}"
    
    @classmethod
    def log_order_operation(cls, operation, order_id=None, user_id=None, details=None):
        """记录订单操作日志"""
        log_data = {
            'operation': operation,
            'order_id': order_id,
            'user_id': user_id,
            'timestamp': datetime.now(),
            'details': details or {}
        }
        
        current_app.logger.info(f"订单操作日志: {log_data}")
        
        # 这里可以将日志写入专门的操作日志表 